import os
import subprocess
from spk.core.workspace import *
from spk.template.act import act_template

class StepDebianWithAct(SpkWorker):
    '''基于 act 来构建 debian 包

    这是一个简单的工作流
    1. 此工作流需要使用 act 工具
    2. 您可以选择现有的 docker 镜像
    3. 您可以自由命令形式提供构建步骤
    4. 最后构建结果将会输出到制品列表
    5. 选择您需要的制品
    6. 下载它
    '''

    First  = "第一步: 选择 act 工作的镜像"
    Second = "第二步: 确认镜像的选择"
    Third  = "第三步: 创建 act 工作流执行配置"
    Fourth = "第四步: 确认工作流的配置"
    Five   = "第五步: 构建结果"
    Six    = "第六步: 选择可下载的制品"
    Seven  = "第七步: 下载制品"


    __steps__ = [
        First, Second, Third, Fourth, Five, Six, Seven
    ]

    def __at__(self, i):
        print('[StepDebianWithAct]: __at__')
        return self.__steps__[i]
    
    __keywords__ = [
        'docker-images',    # 所有可用的 docker 镜像
        'docker-image',     # 选择的 docker 镜像

        'act-workflows',        # 所有可用的 act 工作流
        'act-work-template',    # 工作流配置模板

        'act-build-log',    # 构建日志
        'act-build-code',   # 构建状态码

        'act-artifactsdir', # 存放制品的目录
        'act-artifacts',    # 可供下载的制品
        'act-artifact',     # 已选择的制品
    ]


    def __do_work__(self):
        print('[StepDebianWithAct]: __do_work__')
        super().__do_work__()

        # 第一步: 选择 act 工作的镜像
        if current() == self.First:
            if get('docker-images') is None:

                cmd = 'docker images --format "{{.Repository}}:{{.Tag}}" | grep -v "<none>"'
                # set('docker-images', os.popen(cmd).readlines())
                set('docker-images', subprocess.check_output(cmd, shell=True).decode('utf-8').strip().split('\n'))

            docker_image = st.selectbox('选择镜像', get('docker-images'))

            if st.button('选择'):
                set('docker-image', docker_image)
                next_step()
        else:
            overview, artifacts = st.sidebar.tabs(['工作流配置', '制品列表'])
            with overview:
                image = get('docker-image')
                st.write(f'当前镜像: {image}')
            with artifacts:
                st.table(get('act-artifacts'))

        # 第二步: 确认镜像的选择
        if current() == self.Second:
            prev_step(False)

            if get('docker-image') is None:
                st.warning('请先选择镜像')
            else:
                st.write('您选择的镜像是: ', get('docker-image'))

                next_step(False)
        

        # 第三步: 创建 act 工作流执行配置
        if current() == self.Third:
            prev_step(False)

            if get('act-workflows') is None:
                set('act-workflows', '')
            
            if get('act-artifactsdir') is None:
                set('act-artifactsdir', os.path.join(workdir(), 'artifacts'))


            act_workflows = st.text_area('工作流配置', get('act-workflows'))
            st.text_input('制品查找目录', '/tmp/artifacts', disabled=True)

            if st.button('确认工作流'):
                set('act-workflows', act_workflows)
                next_step()

            st.markdown("""
                    ###### 小提示
                    工作流配置一般为每行一条命令
                    
                    请将制品存放在 `/tmp/artifacts` 目录下, 例如
                    ```
                    cp -r build/*.deb /tmp/artifacts
                    ```
                        """)

        # 第四步: 确认工作流的配置
        if current() == self.Fourth:
            prev_step(False)

            act_work_template = act_template(get('docker-image'), workdir(), get('act-workflows'))

            st.write('当前 Act 工作流配置:')
            st.code(act_work_template, language='yml')

            if st.button('确认开始构建'):
                set('act-work-template', act_work_template)
                next_step()

        # 第五步: 执行构建
        if current() == self.Five:
            prev_step(False)

            st.write('开始构建')

            if get('act-build-log') is None:

                # 1. 准备工作流文件
                workflows = os.path.join(workdir(), ".github/workflows")
                if not os.path.exists(workflows):
                    os.makedirs(workflows, mode=0o755)
                open(os.path.join(workflows, 'spk.yml'), 'w').write(get('act-work-template'))

                # 2. 准备存放 制品 的目录
                artifactsdir = get('act-artifactsdir')
                if not os.path.exists(artifactsdir):
                    os.makedirs(artifactsdir, mode=0o755)

                # 3. 准备与检查 act 命令
                    # 当前目录下存在 spk/bin/act 或 act 时，使用本地的 act 命令
                    # 如果系统中的 act 命令存在，则使用系统自带的 act 命令
                    # 否则，提示用户安装 act 命令，最佳情况就是在后期增加相关的命令选项(把本可以做的事全部做完)
                
                act_path = 'act'
                if os.path.exists('spk/bin/act'):
                    act_path = os.path.join(workdir(), 'act')
                    os.system(f'cp spk/bin/act {workdir()}')
                    os.system(f'chmod +x {workdir()}/act')
                elif os.path.exists('act'):
                    act_path = os.path.join(workdir(), 'act')
                    os.system(f'cp act {workdir()}')
                    os.system(f'chmod +x {workdir()}/act')
                else:
                    if os.system('which act') != 0:
                        st.warning('act 命令不存在, 请先安装 act')
                        st.stop()
                
                # 3. 执行构建, 使用 subprocess 执行 act 命令进行使用离线模式构建
                command = [
                    act_path,
                    '--action-offline-mode',
                ]

                # 执行命令，流式输出
                p = subprocess.Popen(command, 
                                     stdout=subprocess.PIPE, 
                                     stderr=subprocess.STDOUT,
                                     cwd=workdir())
                
                act_build_log = ''

                if get('act-show-tail') is None:
                    set('act-show-tail', True)

                with st.sidebar.expander('操作面板', expanded=True):
                    if st.checkbox('构建日志行限制', get('act-show-tail'), disabled=True):
                        set('act-show-tail', True)
                    else:
                        set('act-show-tail', False)

                with st.spinner('构建中...'):
                    with st.expander('展开构建日志', expanded=True):
                        output = st.code('')
                        while True:
                            out = p.stdout.readline()
                            if out == b'' and p.poll() is not None:
                                break
                            if out:
                                act_build_log += out.strip().decode('utf-8') + '\n'
                                if get('act-show-tail'):
                                    output.code('\n'.join(act_build_log.splitlines()[-20:]), wrap_lines=True)
                                else:
                                    output.code(act_build_log, wrap_lines=True)
                
                # 无效
                # os.system(f'chown -R {os.getuid()}:{os.getgid()} {get('act-artifactsdir')}')

                set('act-build-code', p.returncode)
                set('act-build-log', act_build_log)
                st.rerun()
            else:
                if get('act-build-code') == 0:
                    st.success('构建成功')
                else:
                    st.error('构建失败')
                    with st.expander('构建失败原因'):
                        st.code('\n'.join([line for line in str(get('act-build-log')).splitlines() if 'Error' in line or 'error' in line]))
                
                with st.expander('构建日志'):
                    st.code(get('act-build-log'))

                with st.sidebar.expander('操作面板', expanded=True):
                    if st.button('重新构建'):
                        unset('act-build-code')
                        unset('act-build-log')
                        step(2)
                        st.rerun()
                
                next_step(False)

        # 第六步: 选择制品
        if current() == self.Six:
            prev_step(False)

            artifacts = []
            print(get('act-artifactsdir'))
            for f in os.listdir(get('act-artifactsdir')):
                if f.endswith('.deb'):
                    artifacts.append(f)
            set('act-artifacts', artifacts)

            filename = st.selectbox('选择要下载的制品', get('act-artifacts'))
            
            if filename is None:
                st.warning('没有制品可以选择')
            else:
                if st.button('选择制品'):
                    artifact = os.path.join(get('act-artifactsdir'), filename)
                    set('act-artifact', artifact)
                    next_step()
        
        # 第七步: 下载制品
        if current() == self.Seven:
            prev_step(False)

            if get('act-artifact') is None:
                st.warning('请先选择制品')
            else:
                artifact = get('act-artifact')
                filename = os.path.basename(artifact)

                st.write(f'已选择制品: {artifact}')
                
                st.download_button(label=f'下载制品: {filename}', 
                                    data=open(artifact, 'rb').read(), 
                                    file_name=filename)

        

